package juc;

import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockConditionTest {

    /**
     * 管程（包含了共享变量、操作过程、数据结构）
     */
    static class MyMonitor {

        private static final int MAX = 5;
        // 定义重入锁
        private ReentrantLock rl = new ReentrantLock();
        // 共享区 未满条件
        private Condition notFull = rl.newCondition();
        // 共享区 非空条件
        private Condition notEmpty = rl.newCondition();

        /**
         * 缓冲区
         * 缓冲区用什么数据结构，根据实际需求选择
         */
        private Queue<String> container = new ArrayBlockingQueue<>(50);

        /**
         * 生产方法
         */
        public void produce() {
            rl.lock();
            try {
                // 当共享区大小超过最大值，就不能再生产。未满条件就要阻塞
                if (getSize() >= MAX)
                    notFull.await();
                doProduce();
                // 当共享区有元素时，非空条件满足，唤醒因非空条件阻塞的线程
                if (getSize() >= 1)
                    notEmpty.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                rl.unlock();
            }
        }

        /**
         * 消费方法
         */
        public void consume() {
            rl.lock();
            try {
                // 共享区元素消费完，非空条件就不满足，要阻塞
                if (getSize() == 0)
                    notEmpty.await();
                doConsume();
                // 共享区元素小于MAX最大值，要因未满条件阻塞的线程
                if (getSize() < MAX)
                    notFull.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                rl.unlock();
            }
        }

        private int getSize() {
            return container.size();
        }

        /**
         * 真实生产方法
         */
        private void doProduce() {
            container.offer("product");
            System.out.println("生产者生产product到缓冲区,当前大小：" + container.size() + "  当前时间：" + System.currentTimeMillis());
        }

        /**
         * 真实消费方法方法
         */
        private void doConsume(){
            container.poll();
            System.out.println("消费者从缓冲区消费product,当前大小：" + container.size() + "  当前时间：" + System.currentTimeMillis());
        }
    }


	/**
	 * 主线程，启动生产者和消费者
	 * @param args
	 */
	public static void main(String[] args) {
		MyMonitor myMonitor = new MyMonitor();

		Thread produce = new Thread(() -> {
			while (true) {  // 生产者线程
				myMonitor.produce();
			}
		});
		Thread consume = new Thread(() -> {
			while (true) {  // 消费者线程
				myMonitor.consume();
			}

		});
		consume.start();
		produce.start();
	}
}
